package com.guxingke.gsb.render;

import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache;
import com.github.jknack.handlebars.io.FileTemplateLoader;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.guxingke.gsb.config.Config;
import com.guxingke.gsb.config.Constant;
import com.guxingke.gsb.io.FileAccess;
import com.guxingke.gsb.model.IParseable;
import com.guxingke.gsb.model.IndexModel;
import com.guxingke.gsb.model.Page;
import com.guxingke.gsb.model.Post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by 孤星可 on 2015/11/18.
 *
 * @author guxingke
 * @since 2015/11/18
 */
public class HandlebarsRender implements IRender {
    private static final Logger logger = LoggerFactory.getLogger(HandlebarsRender.class);
    private TemplateLoader loader;
    private Handlebars handlebars;
    private FileAccess fileAccess;

    private Map<String, Template> templateMap;

    private static HandlebarsRender render;

    private HandlebarsRender(String templatePath, String suffix) throws IOException {
        fileAccess = new FileAccess();
        loader = new FileTemplateLoader(templatePath, suffix);
        handlebars = new Handlebars(loader).with(new ConcurrentMapTemplateCache());
        templateMap = new HashMap<>();
        templateMap.put("post", handlebars.compile("post"));
        templateMap.put("page", handlebars.compile("page"));
        templateMap.put("index", handlebars.compile("index"));
    }

    public static HandlebarsRender getInstance(String templatePath, String suffix) {
        if (null == render) {
            try {
                render = new HandlebarsRender(templatePath, suffix);
            } catch (IOException e) {
                logger.error("init render failed", e);
            }
        }
        return render;
    }

    @Override
    public String renderParseable(IParseable parseable) {
        Map<String, Object> map = new HashMap<>();
        map.put("post", parseable);
        map.put("config", Config.getThemeConfig());
        String layout = parseable.getLayout();
        Template template = templateMap.get(layout);

        try {
            return template.apply(map);
        } catch (IOException e) {
            logger.error("render failed.object:{}", parseable, e);
        }
        return null;
    }

    public String renderParseable(Map<String, Object> data, Template template) {
        try {
            return template.apply(data);
        } catch (IOException e) {
            logger.error("render failed. data:{}", data, e);
        }
        return null;
    }

    public boolean renderPosts(List<Post> posts) {
        boolean result = true;
        for (int i = 0; i < posts.size(); i++) {
            String s = renderPost(posts, i);
            if (null != s) {
                Post post = posts.get(i);
                String slug = post.getSlug();
                result &= writeParseable(slug, s);
            }
        }
        return result;
    }

    private String renderPost(List<Post> posts, int index) {
        Template template = templateMap.get("post");
        Map<String, Object> data = null;
        Post preview = null;
        Post next = null;
        data = new HashMap<>();
        if (index > 0) {
            preview = posts.get(index - 1);
        }
        if (index + 1 < posts.size()) {
            next = posts.get(index + 1);
        }
        data.put("previous", preview);
        data.put("post", posts.get(index));
        data.put("next", next);
        data.put("config", Config.getThemeConfig());

        return renderParseable(data, template);
    }

    public boolean renderPages(List<Page> pages) {
        boolean result = true;
        for (int i = 0; i < pages.size(); i++) {
            Page page = pages.get(i);
            String content = renderPage(page);
            if (null != content) {
                String slug = page.getSlug();
                result &= writeParseable(slug, content);
            }
        }
        return result;
    }

    public boolean renderIndex(List<IndexModel> indexModels) {
        boolean result = true;
        for (int i = 0; i < indexModels.size(); i++) {
            IndexModel indexModel = indexModels.get(i);
            String content = renderIndex(indexModel);
            if (null == content) {
                continue;
            }
            result &= writeIndex(i + 1, content);
        }
        return result;
    }

    private boolean writeIndex(int page, String content) {
        String sitePath = Config.getSiteConfigPath();
        Path path = null;
        if (page == 1) {
            path = Paths.get(sitePath, Constant.OUTPUT_DIRECTORY);
        } else {
            path = Paths.get(sitePath, "output", page + "");
        }
        return fileAccess.write(path, "index.html", content);
    }

    private boolean writeParseable(String slug, String content) {
        String sitePath = Config.getSiteConfigPath();
        Path path = Paths.get(sitePath, "output", slug);
        return fileAccess.write(path, "index.html", content);
    }

    private String renderPage(Page page) {
        Template template = templateMap.get("page");
        Map<String, Object> data = new HashMap<>();
        data.put("page", page);
        data.put("config", Config.getThemeConfig());
        String content = renderParseable(data, template);
        return content;
    }

    private String renderIndex(IndexModel indexModel) {
        Template template = templateMap.get("index");
        Map<String, Object> data = new HashMap<>();
        data.put("index", indexModel);
        data.put("config", Config.getThemeConfig());
        String content = renderParseable(data, template);
        return content;
    }
}
